package net.joint_painting.client.controller;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.joint_painting.client.model.Model;
import net.joint_painting.client.observer.TimeObserver;
import net.joint_painting.shared.model.Line;
import net.joint_painting.shared.model.Work;
import net.joint_painting.shared.service.Service;
import net.joint_painting.shared.service.ServiceAsync;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * @author CAM
 * @version $Revision: 1.0 $
 */
public class NetworkController implements TimeObserver {

    private Model model;

    private final ServiceAsync service = GWT.create(Service.class);

    Map<String, List<Line>> buffer = new HashMap<String, List<Line>>();
    Set<String> waitList = new HashSet<String>();
    int startGetLines = 0;
    Boolean sending = false;

    public NetworkController() {

    }

    /**
     * Method drawLine.
     * 
     * @param line
     *            Line
     */
    public void drawLine(Line line) {
	List<Line> lines = buffer.get(line.getSurfaceId());
	if (lines == null)
	    lines = new LinkedList<Line>();
	lines.add(line);
	buffer.put(line.getSurfaceId(), lines);
    }

    private void drawService() {
	if (buffer.size() > 0 && sending.equals(false)) {
	    final String mapKey = (String) buffer.keySet().toArray()[0];
	    final List<Line> lines = buffer.get(mapKey);
	    buffer.remove(mapKey);
	    sending = true;
	    service.drawLines(lines, new AsyncCallback() {

		@Override
		public void onFailure(Throwable caught) {
		    buffer.put(mapKey, lines);
		    sending = false;
		}

		@Override
		public void onSuccess(Object result) {
		    sending = false;
		}

	    });
	}
    }

    /**
     * Method getSurfaceArea.
     * 
     * @param areaX
     *            Integer
     * @param areaY
     *            Integer
     */
    public void getSurfaceArea(Integer areaX, Integer areaY) {
	waitList.add(net.joint_painting.shared.logic.Surface.getSurfaceID(
		model.getId(), areaX, areaY));
    }

    /**
     * Method getSurfaceArea.
     * 
     * @param id
     *            String
     */
    public void getSurfaceArea(String id) {
	getSurfaceArea(net.joint_painting.shared.logic.Surface.getXFromID(id),
		net.joint_painting.shared.logic.Surface.getYFromID(id));
    }

    private void getSurfaces() {
	if (waitList.size() > 0) {
	    final String mapKey = (String) waitList.toArray()[0];
	    waitList.remove(mapKey);
	    service.getSurface(mapKey, new AsyncCallback<String>() {

		@Override
		public void onFailure(Throwable caught) {
		}

		@Override
		public void onSuccess(String result) {
		    model.addSurface(mapKey, result);
		}
	    });
	}
    }

    private void getUpdates() {
	service.getUpdatedSurfaces(new AsyncCallback<List<String>>() {

	    @Override
	    public void onFailure(Throwable caught) {
	    }

	    @Override
	    public void onSuccess(List<String> result) {
		waitList.addAll(result);
	    }

	});
    }

    private void getWork() {
	service.getWork(new AsyncCallback<Work>() {

	    @Override
	    public void onFailure(Throwable caught) {
	    }

	    @Override
	    public void onSuccess(Work result) {
		if (result == null)
		    return;
		net.joint_painting.client.logic.Worker.doWork(result);
		service.sendWork(result, new AsyncCallback() {

		    @Override
		    public void onFailure(Throwable caught) {
		    }

		    @Override
		    public void onSuccess(Object result) {
		    }

		});
	    }

	});
    }

    /**
     * Method setModel.
     * 
     * @param model
     *            Model
     */
    public void setModel(Model model) {
	this.model = model;
    }

    /**
     * Method update.
     * 
     * @param elapsed
     *            Integer
     * 
     * @see net.joint_painting.client.observer.TimeObserver#update(Integer)
     */
    @Override
    public void update(Integer elapsed) {
	if (elapsed % 5 == 0)
	    getSurfaces();
	if (elapsed % 150 == 0)
	    drawService();
	if (elapsed % 2500 == 0)
	    getUpdates();
	if (elapsed % 100 == 0)
	    getWork();
    }
}
